﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Threading;
using Xceed.Silverlight.Data;
using Xceed.Silverlight.ListBox.Utils;
using Xceed.Silverlight.Utils;
using Xceed.Silverlight.Controls;
using Xceed.Silverlight.DragDrop;
using System.Windows.Documents;

namespace Xceed.Silverlight.ListBox
{
  [TemplateVisualState( GroupName = "CommonStates", Name = "Disabled" )]
  [TemplateVisualState( GroupName = "CommonStates", Name = "Normal" )]
  [TemplateVisualState( GroupName = "ResquestStatus", Name = "RequestsPending" )]
  [TemplateVisualState( GroupName = "ResquestStatus", Name = "NoPendingRequests" )]
  [TemplateVisualState( GroupName = "LicensingStates", Name = "Licensed" )]
  [TemplateVisualState( GroupName = "LicensingStates", Name = "NotLicensed" )]
  [TemplateVisualState( GroupName = "DataNavigationPaneStates", Name = "DataNavigationPaneVisible" )]
  [TemplateVisualState( GroupName = "DataNavigationPaneStates", Name = "DataNavigationPaneHidden" )]
  [TemplatePart( Name = "ScrollViewer", Type = typeof( ScrollViewer ) )]
  [ContentProperty( "Items" )]
  public partial class ListBox : Control,
    INotifyPropertyChanged,
    ISupportInitialize,
    IWeakEventListener,
    IDataNavigationHost,
    IDropTarget
  {
    #region STATIC MEMBERS

    private const string DisplayMemberTemplateXAML =
      @"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
      <ContentPresenter Content=""{Binding %PATH%}""/>
      </DataTemplate>";

    private static ContainerConfigurations DefaultContainerConfigurations
    {
      get
      {
        if( DefaultContainerConfigurationsCache == null )
        {
          DefaultContainerConfigurationsCache = new ContainerConfigurations();
        }

        return DefaultContainerConfigurationsCache;
      }
    }

    private static ContainerConfigurations DefaultContainerConfigurationsCache;

    #endregion STATIC MEMBERS

    #region Constructors

    public ListBox()
    {
      // Some features must be disabled in design mode.
      m_isInDesigner = ( Xceed.Silverlight.Compatibility.CompatibilityUtils.IsInDesignTool )
                    || ( Xceed.Utils.Licensing.DesignerProperties.GetIsInDesignMode() )
                    || ( System.ComponentModel.DesignerProperties.GetIsInDesignMode( this ) );

      // Lock the scrolling if the ListBox is in designer mode
      this.IsLocked = m_isInDesigner;



      this.DefaultStyleKey = typeof( ListBox );

      // While we have no ItemsSource, we consider ourselves Unbound.
      this.IsUnbound = true;

      m_items = new ItemsWrapper( this );

      m_selectionManager = new SelectionManager( this );
      m_selectionManager.SelectionChanged += this.OnSelectionManagerSelectionChanged;
      m_selectionManager.UpdateSelectionProperties += this.OnSelectionManagerUpdateSelectionProperties;

      m_selectedItems = new SelectedItemsWrapper( this );
      m_selectedItems.Completed += this.OnSelectedItemsCompleted;
      ObservableCollection<SelectionRange> selectedRanges = new ObservableCollection<SelectionRange>();
      selectedRanges.CollectionChanged += this.OnSelectedRangesChanged;
      this.SelectedRanges = selectedRanges;

      Binding binding = new Binding();
      binding.RelativeSource = new RelativeSource( RelativeSourceMode.Self );
      binding.Path = new PropertyPath( "AllowDrop" );
      this.SetBinding( ListBox.InternalAllowDropProperty, binding );

      DragDropManager.IsDraggedElementConfigurationPreserved = true;

#if !SILVERLIGHT
      this.ContainerConfigurations = new ContainerConfigurations();
      this.AnimationSettings = new ListBoxAnimationSettings();
#endif

      this.Loaded += this.OnLoaded;
      this.Unloaded += this.OnUnLoaded;
      this.IsEnabledChanged += this.OnIsEnabledChanged;
    }

    #endregion Constructors

    #region FilterExpression Property

    public static readonly DependencyProperty FilterExpressionProperty = DependencyProperty.Register(
      "FilterExpression",
      typeof( FilterExpressionBase ),
      typeof( ListBox ),
      new PropertyMetadata(
        null,
        new PropertyChangedCallback( ListBox.OnFilterExpressionChanged ) ) );

    public FilterExpressionBase FilterExpression
    {
      get
      {
        return ( FilterExpressionBase )this.GetValue( ListBox.FilterExpressionProperty );
      }
      set
      {
        this.SetValue( ListBox.FilterExpressionProperty, value );
      }
    }

    private static void OnFilterExpressionChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = sender as ListBox;

      // Never apply Grouping, Sorting, or Filtering to an unbound listbox.
      // Else the list is untyped and the data source wrapper won't be able to find
      // the properties corresponding to the grouping, sorting, or filtering criterias
      // resulting in a crash.
      if( ( listBox == null ) || ( !listBox.IsInitialized ) || ( listBox.IsUnbound ) )
        return;

      listBox.Reset();
    }

    #endregion

    #region Filter Event

    public event EventHandler<ListBoxFilterEventArgs> Filter
    {
      add
      {
        m_filter = this.GeneratePredicateForFilter( value );
        this.ResetOnFilterPredicateChanged();
      }
      remove
      {
        m_filter = null;
        this.ResetOnFilterPredicateChanged();
      }
    }

    private void ResetOnFilterPredicateChanged()
    {
      if( !this.IsInitialized )
        return;

      this.ClearDataSourceEnumeratorWrapper();

      this.UpdateSelectedItemAndSelectedItems();

      this.DeferRefresh();
    }

    private Predicate<object> GeneratePredicateForFilter( EventHandler<ListBoxFilterEventArgs> handler )
    {
      if( handler == null )
        return null;

      return new Predicate<object>( ( item ) =>
      {
        Debug.Assert( !( item is DataItem ), "A DataItem was the item passed as a parameter." );

        ListBoxFilterEventArgs eventArgs = new ListBoxFilterEventArgs( item );

        handler.Invoke( this, eventArgs );

        return eventArgs.Accepted;
      } );
    }

    #endregion

    #region DisplayMemberPath Property

    public static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register(
      "DisplayMemberPath",
      typeof( string ),
      typeof( ListBox ),
      new PropertyMetadata( null, new PropertyChangedCallback( ListBox.OnDisplayMemberPathChanged ) ) );

    public string DisplayMemberPath
    {
      get
      {
        return ( string )this.GetValue( ListBox.DisplayMemberPathProperty );
      }
      set
      {
        this.SetValue( ListBox.DisplayMemberPathProperty, value );
      }
    }

    private static void OnDisplayMemberPathChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = sender as ListBox;

      if( listBox == null )
        return;

      if( listBox.ItemTemplate != null )
        throw new InvalidOperationException( "Cannot set ItemTemplate and DisplayMemberPath simultaneously." );

      listBox.InvalidateContainerConfigurationCache( ConfigurationType.Item );
    }

    #endregion DisplayMemberPath Property

    #region IsUnbound Internal Property

    internal bool IsUnbound
    {
      get;
      set;
    }

    #endregion

    #region ItemContainerStyle Property

    public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register(
      "ItemContainerStyle",
      typeof( Style ),
      typeof( ListBox ),
      new PropertyMetadata(
        null,
        new PropertyChangedCallback( ListBox.OnItemContainerStyleChanged ) ) );

    public Style ItemContainerStyle
    {
      get
      {
        return ( Style )this.GetValue( ListBox.ItemContainerStyleProperty );
      }
      set
      {
        this.SetValue( ListBox.ItemContainerStyleProperty, value );
      }
    }

    private static void OnItemContainerStyleChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = sender as ListBox;

      if( listBox == null )
        return;

      listBox.InvalidateContainerConfigurationCache( ConfigurationType.Item );
    }

    #endregion ItemContainerStyle Property

    #region ItemScrollingBehavior Property

    public static readonly DependencyProperty ItemScrollingBehaviorProperty = DependencyProperty.Register(
      "ItemScrollingBehavior",
      typeof( ItemScrollingBehavior ),
      typeof( ListBox ),
      new PropertyMetadata(
        ItemScrollingBehavior.Immediate,
        new PropertyChangedCallback( ListBox.OnItemScrollingBehaviorChanged ) ) );

    public ItemScrollingBehavior ItemScrollingBehavior
    {
      get
      {
        return ( ItemScrollingBehavior )this.GetValue( ListBox.ItemScrollingBehaviorProperty );
      }
      set
      {
        this.SetValue( ListBox.ItemScrollingBehaviorProperty, value );
      }
    }

    private static void OnItemScrollingBehaviorChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
#if !SILVERLIGHT
      ListBox listBox = sender as ListBox;

      if( listBox == null )
        return;

      if( listBox.ItemScrollingBehavior == ItemScrollingBehavior.Deferred )
      {
        ScrollViewer.SetIsDeferredScrollingEnabled( listBox, true );
      }
      else
      {
        listBox.ClearValue( ScrollViewer.IsDeferredScrollingEnabledProperty );
      }
#endif
    }

    #endregion

    #region Items Property

    public IList Items
    {
      get
      {
        Debug.Assert( m_items != null );
        return m_items;
      }
    }

    #endregion Items Property

    #region ItemsPanel Property

    public static readonly DependencyProperty ItemsPanelProperty = DependencyProperty.Register(
      "ItemsPanel",
      typeof( ItemsPanelTemplate ),
      typeof( ListBox ),
      new PropertyMetadata( null ) );

    public ItemsPanelTemplate ItemsPanel
    {
      get
      {
        return ( ItemsPanelTemplate )this.GetValue( ListBox.ItemsPanelProperty );
      }
      set
      {
        this.SetValue( ListBox.ItemsPanelProperty, value );
      }
    }

    #endregion ItemsPanel Property

    #region ItemsSource Property

    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
      "ItemsSource",
      typeof( object ),
      typeof( ListBox ),
      new PropertyMetadata(
        null,
        new PropertyChangedCallback( ListBox.OnItemsSourceChanged ) ) );

    public object ItemsSource
    {
      get
      {
        return this.GetValue( ListBox.ItemsSourceProperty );
      }
      set
      {
        this.SetValue( ListBox.ItemsSourceProperty, value );
      }
    }

    private static void OnItemsSourceChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = sender as ListBox;

      if( listBox == null )
        return;

      listBox.Reset();
    }

    #endregion ItemsSource Property

    #region ItemTemplate Property

    public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
      "ItemTemplate",
      typeof( DataTemplate ),
      typeof( ListBox ),
      new PropertyMetadata( null, new PropertyChangedCallback( ListBox.OnItemTemplateChanged ) ) );

    public DataTemplate ItemTemplate
    {
      get
      {
        return ( DataTemplate )this.GetValue( ListBox.ItemTemplateProperty );
      }
      set
      {
        this.SetValue( ListBox.ItemTemplateProperty, value );
      }
    }

    private static void OnItemTemplateChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = sender as ListBox;

      if( listBox == null )
        return;

      if( !String.IsNullOrEmpty( listBox.DisplayMemberPath ) )
        throw new InvalidOperationException( "Cannot set ItemTemplate and DisplayMemberPath simultaneously." );

      listBox.InvalidateContainerConfigurationCache( ConfigurationType.Item );

      listBox.OnItemTemplateChanged( e.OldValue as DataTemplate, e.NewValue as DataTemplate );
    }

    #endregion ItemTemplate Property

    #region ContainerConfigurations Property

#if SILVERLIGHT
    public static readonly DependencyProperty ContainerConfigurationsProperty = DependencyProperty.Register(
       "ContainerConfigurations",
       typeof( ContainerConfigurations ),
       typeof( ListBox ),
       new PropertyMetadata( new ContainerConfigurations(), new PropertyChangedCallback( ListBox.OnContainerConfigurationsChanged ) ) );
#else
    public static readonly DependencyProperty ContainerConfigurationsProperty = DependencyProperty.Register(
       "ContainerConfigurations",
       typeof( ContainerConfigurations ),
       typeof( ListBox ),
       new PropertyMetadata( null, new PropertyChangedCallback( ListBox.OnContainerConfigurationsChanged ) ) );
#endif

    public ContainerConfigurations ContainerConfigurations
    {
      get
      {
        return ( ContainerConfigurations )this.GetValue( ListBox.ContainerConfigurationsProperty );
      }
      set
      {
        this.SetValue( ListBox.ContainerConfigurationsProperty, value );
      }
    }

    private static void OnContainerConfigurationsChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = ( ListBox )sender;

      ContainerConfigurations oldContainerConfigurations = e.OldValue as ContainerConfigurations;
      ContainerConfigurations newContainerConfigurations = e.NewValue as ContainerConfigurations;

      if( oldContainerConfigurations != null )
      {
        ContainerConfigurationsInvalidatedManager.RemoveListener( oldContainerConfigurations, listBox );
      }

      if( newContainerConfigurations != null )
      {
        ContainerConfigurationsInvalidatedManager.AddListener( newContainerConfigurations, listBox );
      }

      // Invalidating the ContainerConfigurations also clears automatically the
      // Container Configuration, Container ContentTemplate, and Container Style for every thing.
      listBox.ClearContainerConfigurationCache();

    }

    #endregion ContainerConfigurations Property

    #region IsAsyncDataSource Internal Read-Only Property

    internal bool IsAsyncDataSource
    {
      get;
      private set;
    }

    #endregion

    #region GroupDescriptions Property

    public ObservableCollection<GroupDescription> GroupDescriptions
    {
      get
      {
        if( m_groupDescriptions == null )
        {
          m_groupDescriptions = new ObservableCollection<GroupDescription>();
          m_groupDescriptions.CollectionChanged += this.OnGroupDescriptions_CollectionChanged;
        }

        return m_groupDescriptions;
      }
    }

    private void OnGroupDescriptions_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
    {
      // Never apply Grouping, Sorting, or Filtering to an unbound listbox.
      // Else the list is untyped and the data source wrapper won't be able to find
      // the properties corresponding to the grouping, sorting, or filtering criterias
      // resulting in a crash.
      if( ( !this.IsInitialized ) || ( this.IsUnbound ) )
        return;

      this.UpdateDataSourceWrapperGroupDescriptions();
      this.DeferRefresh();
    }

    private ObservableCollection<GroupDescription> m_groupDescriptions;

    #endregion

    #region SortDescriptions Property

    public ObservableCollection<SortDescription> SortDescriptions
    {
      get
      {
        if( m_sortDescriptions == null )
        {
          m_sortDescriptions = new ObservableCollection<SortDescription>();
          m_sortDescriptions.CollectionChanged += this.OnSortDescriptions_CollectionChanged;
        }

        return m_sortDescriptions;
      }
    }

    private void OnSortDescriptions_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
    {
      // Never apply Grouping, Sorting, or Filtering to an unbound listbox.
      // Else the list is untyped and the data source wrapper won't be able to find
      // the properties corresponding to the grouping, sorting, or filtering criterias
      // resulting in a crash.
      if( ( !this.IsInitialized ) || ( this.IsUnbound ) )
        return;

      this.UpdateDataSourceWrapperSortDescriptions();
      this.DeferRefresh();
    }

    private ObservableCollection<SortDescription> m_sortDescriptions;

    #endregion

    #region ListBoxAnimationSettings Property

#if SILVERLIGHT
    public static readonly DependencyProperty AnimationSettingsProperty = DependencyProperty.Register(
      "AnimationSettings",
      typeof( ListBoxAnimationSettings ),
      typeof( ListBox ),
      new PropertyMetadata( new ListBoxAnimationSettings() ) );
#else
    public static readonly DependencyProperty AnimationSettingsProperty = DependencyProperty.Register(
      "AnimationSettings",
      typeof( ListBoxAnimationSettings ),
      typeof( ListBox ),
      new PropertyMetadata( null ) );
#endif

    public ListBoxAnimationSettings AnimationSettings
    {
      get
      {
        return ( ListBoxAnimationSettings )this.GetValue( ListBox.AnimationSettingsProperty );
      }
      set
      {
        this.SetValue( ListBox.AnimationSettingsProperty, value );
      }
    }

    #endregion

    #region Panel Internal Property

    internal PanelBase Panel
    {
      get
      {
        return m_panel;
      }
      private set
      {
        if( m_panel != value )
        {
          m_panel = value;
          this.OnPanelChanged();
        }
      }
    }

    private PanelBase m_panel;

    internal void OnPanelChanged()
    {
      if( m_previousView != null )
        m_previousView.RaiseCanExecuteChanged();

      if( m_nextView != null )
        m_nextView.RaiseCanExecuteChanged();
    }

    #endregion

    #region CurrentDataPath Internal Property

    internal DataPath CurrentDataPath
    {
      get;
      set;
    }

    #endregion

    #region IsUpdateSelectedItemsAndItemInhibited
    internal bool IsUpdateSelectedItemsAndItemInhibited
    {
      get
      {
        return ( m_inhibitUpdateSelectedItemAndItemsCount > 0 );
      }
    }
    #endregion

    #region SelectedItem Property

    public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
      "SelectedItem",
      typeof( object ),
      typeof( ListBox ),
      new PropertyMetadata(
        null,
        new PropertyChangedCallback( ListBox.OnSelectedItemChanged ) ) );

    public object SelectedItem
    {
      get
      {
        return ( object )this.GetValue( ListBox.SelectedItemProperty );
      }
      set
      {
        this.SetValue( ListBox.SelectedItemProperty, value );
      }
    }

    private static void OnSelectedItemChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = sender as ListBox;

      if( listBox == null )
        return;

      if( ( !listBox.IsUpdateSelectedItemsAndItemInhibited )
        && ( listBox.DataSourceWrapper != null )
        && ( listBox.DataSourceWrapper.IsPrimaryKeyVolatile ) )
        throw new NotSupportedException( "Cannot set the selected item with the current data source." );

      if( !listBox.m_updatingSelectedItemFromSelectedValue )
      {
        listBox.UpdateSelectedValueFromSelectedItem();
      }

      listBox.UpdateSelectedRangesFromSelectedItem();
    }

    private void UpdateSelectedValueFromSelectedItem()
    {
      if( String.IsNullOrEmpty( m_cachedSelectedValuePath ) )
        return;

      Debug.Assert( !m_updatingSelectedValueFromSelectedItem );
      m_updatingSelectedValueFromSelectedItem = true;
      try
      {
        object selectedItem = this.SelectedItem;

        if( selectedItem == null )
        {
          this.SelectedValue = null;
          return;
        }

        this.SelectedValue = this.SelectedValuePathExtractor.GetValueFromItem( selectedItem );
      }
      finally
      {
        m_updatingSelectedValueFromSelectedItem = false;
      }
    }

    #endregion

    #region SelectedItems Property

    public IList SelectedItems
    {
      get
      {
        Debug.Assert( m_selectedItems != null );
        return m_selectedItems;
      }
    }

    #endregion

    #region SelectionManager Internal Property

    internal SelectionManager SelectionManager
    {
      get
      {
        return m_selectionManager;
      }
    }

    #endregion

    #region SelectionMode Property

    public static readonly DependencyProperty SelectionModeProperty = DependencyProperty.Register(
      "SelectionMode",
      typeof( SelectionMode ),
      typeof( ListBox ),
      new PropertyMetadata(
        SelectionMode.Single,
        new PropertyChangedCallback( ListBox.OnSelectionModeChanged ) ) );

    public SelectionMode SelectionMode
    {
      get
      {
        return ( SelectionMode )this.GetValue( ListBox.SelectionModeProperty );
      }
      set
      {
        this.SetValue( ListBox.SelectionModeProperty, value );
      }
    }

    private static void OnSelectionModeChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = sender as ListBox;

      if( listBox == null )
        return;

      listBox.m_selectionManager.SelectionMode = ( SelectionMode )e.NewValue;
    }

    #endregion

    #region SelectedRanges Property

    public IList<SelectionRange> SelectedRanges
    {
      get;
      private set;
    }

    private void OnSelectedRangesChanged( object sender, NotifyCollectionChangedEventArgs e )
    {
      IList selectedRanges = sender as IList;

      switch( e.Action )
      {

        case NotifyCollectionChangedAction.Add:
        case NotifyCollectionChangedAction.Replace:
          this.HandleSelectionRangeAddition( e.NewItems );
          break;

        case NotifyCollectionChangedAction.Reset:

          if( selectedRanges.Count > 0 )
          {
            m_selectionManager.UnselectAllHelper();
          }
          else
          {
            // We must update the VisualState of every containers
            // in the Panel since no more SelectionRange are
            // present in the SelectedRanges
            PanelBase panel = this.Panel;

            if( panel != null )
              panel.UpdateSelectionPropertiesOnContainers();

            return;
          }
          break;

        case NotifyCollectionChangedAction.Remove:
        default:
          break;
      }

      if( !m_selectionManager.IsUpdatingSelection )
      {
        m_selectionManager.NotifySelectionChanged();
      }
    }

    private void HandleSelectionRangeAddition( IList addedSelectionRanges )
    {
      DataSourceWrapper dataSource = this.DataSourceWrapper;

      Debug.Assert( dataSource != null );
      if( dataSource == null )
        return;

      IDataSourceWrapperHelper dataHelper = dataSource.DataHelper;
      bool isPrimaryKeyVolatile = dataSource.IsPrimaryKeyVolatile;

      foreach( SelectionRange selectionRange in addedSelectionRanges )
      {
        DataRangeContext context;

        if( selectionRange.UseDefaultDataRangeContext )
        {
          context = dataSource.GetCurrentDataRangeContext();
        }
        else
        {
          IEnumerable<SortDescription> sortDescriptions = selectionRange.SortDescriptions;
          if( selectionRange.SortDescriptions.Count == 0 )
          {
            sortDescriptions = dataSource.AppliedSortDescriptions;
          }

          context = new DataRangeContext( dataSource.ElementType, sortDescriptions, selectionRange.Filter, selectionRange.FilterExpression );
        }

        selectionRange.Freeze( context, dataHelper, isPrimaryKeyVolatile );
      }
    }

    #endregion

    #region SelectedValue Property

    public static readonly DependencyProperty SelectedValueProperty =
        DependencyProperty.Register( "SelectedValue", typeof( object ), typeof( ListBox ),
        new PropertyMetadata( null, new PropertyChangedCallback( ListBox.OnSelectedValueChanged ) ) );

    public object SelectedValue
    {
      get { return this.GetValue( ListBox.SelectedValueProperty ); }
      set { this.SetValue( ListBox.SelectedValueProperty, value ); }
    }

    private static void OnSelectedValueChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = ( ListBox )sender;

      if( !listBox.m_updatingSelectedValueFromSelectedItem )
      {
        // The change does not come from an explicit call to update the SelectedValue from the SelectedItem.
        // We must update the SelectedItem from the new SelectedValue.
        listBox.UpdateSelectedItemFromSelectedValue();
      }
    }

    private void UpdateSelectedItemFromSelectedValue()
    {
      Debug.Assert( !m_updatingSelectedItemFromSelectedValue );
      m_updatingSelectedItemFromSelectedValue = true;
      try
      {
        if( this.IsAsyncDataSource )
          throw new NotSupportedException( "Setting SelectedValue is not supported while bound to an asynchronous source." );

        if( ( this.DataSourceWrapper != null )
           && ( this.DataSourceWrapper.IsPrimaryKeyVolatile ) )
          throw new NotSupportedException( "Setting SelectedValue is not supported with the current data source." );

        object selectedValue = this.SelectedValue;

        if( selectedValue == null )
        {
          this.SelectedItem = null;
          return;
        }

        BindingPathValueExtractor selectedValuePathExtractor = this.SelectedValuePathExtractor;

        // Loop in the items to find a match.
        DataSourceEnumeratorWrapper syncEnumerator = m_dataSourceWrapper.GetSyncDataEnumerator();
        while( syncEnumerator.MoveNext() )
        {
          DataPath dataPath = syncEnumerator.Current;
          object item = m_dataSourceWrapper.DataHelper.ConvertBack( dataPath );
          object itemValue = selectedValuePathExtractor.GetValueFromItem( item );

          if( object.Equals( itemValue, selectedValue ) )
          {
            this.SelectedItem = item;
            return;
          }
        }

        // No matches were found.
        this.SelectedItem = null;
      }
      finally
      {
        m_updatingSelectedItemFromSelectedValue = false;
      }
    }

    #endregion

    #region SelectedValuePath Property

    public static readonly DependencyProperty SelectedValuePathProperty =
        DependencyProperty.Register( "SelectedValuePath", typeof( object ), typeof( ListBox ),
        new PropertyMetadata( null, new PropertyChangedCallback( ListBox.OnSelectedValuePathChanged ) ) );

    public string SelectedValuePath
    {
      get { return ( string )this.GetValue( ListBox.SelectedValuePathProperty ); }
      set { this.SetValue( ListBox.SelectedValuePathProperty, value ); }
    }

    private static void OnSelectedValuePathChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = ( ListBox )sender;

      listBox.m_cachedSelectedValuePath = e.NewValue as string;

      // Setting the BindingPathExtractor to null will make the next SelectedValuePathExtractor property getter
      // create a new instance for the new SelectedValuePath.
      listBox.m_selectedValuePathExtractor = null;


      // Refresh the SelectedVlaue
      listBox.UpdateSelectedValueFromSelectedItem();
    }

    #endregion

    #region ToolPaneVisibility

    public static readonly DependencyProperty ToolPaneVisibilityProperty = DependencyProperty.Register( "ToolPaneVisibility",
      typeof( Visibility ),
      typeof( ListBox ),
      new PropertyMetadata( Visibility.Visible ) );

    public Visibility ToolPaneVisibility
    {
      get
      {
        return ( Visibility )this.GetValue( ListBox.ToolPaneVisibilityProperty );
      }
      set
      {
        this.SetValue( ListBox.ToolPaneVisibilityProperty, value );
      }
    }

    #endregion

    #region HorizontalScrollBarVisibility Property

    public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = DependencyProperty.Register(
      "HorizontalScrollBarVisibility",
      typeof( ScrollBarVisibility ),
      typeof( ListBox ),
      new PropertyMetadata( ( ScrollBarVisibility )ScrollBarVisibility.Auto ) );

    public ScrollBarVisibility HorizontalScrollBarVisibility
    {
      get
      {
        return ( ScrollBarVisibility )this.GetValue( ListBox.HorizontalScrollBarVisibilityProperty );
      }
      set
      {
        this.SetValue( ListBox.HorizontalScrollBarVisibilityProperty, value );
      }
    }

    #endregion

    #region VerticalScrollBarVisibility Property

    public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = DependencyProperty.Register(
      "VerticalScrollBarVisibility",
      typeof( ScrollBarVisibility ),
      typeof( ListBox ),
      new PropertyMetadata( ( ScrollBarVisibility )ScrollBarVisibility.Auto ) );

    public ScrollBarVisibility VerticalScrollBarVisibility
    {
      get
      {
        return ( ScrollBarVisibility )this.GetValue( ListBox.VerticalScrollBarVisibilityProperty );
      }
      set
      {
        this.SetValue( ListBox.VerticalScrollBarVisibilityProperty, value );
      }
    }

    #endregion

    #region NextViewCommand Property

    public ICommand NextViewCommand
    {
      get
      {
        if( m_nextView == null )
        {
          m_nextView = new Command( new Action<object>( this.ExecuteNextView ), new Func<object, bool>( this.CanExecuteNextView ) );
        }

        return m_nextView;
      }
    }

    #endregion

    #region PreviousViewCommand Property

    public ICommand PreviousViewCommand
    {
      get
      {
        if( m_previousView == null )
        {
          m_previousView = new Command( new Action<object>( this.ExecutePreviousView ), new Func<object, bool>( this.CanExecutePreviousView ) );
        }

        return m_previousView;
      }
    }

    #endregion

    #region IsLoaded Internal Property

    internal bool IsLoaded
    {
      get
      {
        return m_isLoaded;
      }
    }

    #endregion

    #region DataSourceWrapper Internal Property

    internal DataSourceWrapper DataSourceWrapper
    {
      get
      {
        if( m_dataSourceWrapper == null )
          this.UpdateDataSourceAndDataSourceEnumerator();

        return m_dataSourceWrapper;
      }
    }

    #endregion

    #region DataSourceEnumeratorWrapper Internal Property

    internal DataSourceEnumeratorWrapper DataSourceEnumeratorWrapper
    {
      get
      {
        if( m_dataSourceEnumeratorWrapper == null )
          this.UpdateDataSourceAndDataSourceEnumerator();

        if( !m_dataEnumeratorInitialized )
        {
          m_dataSourceEnumeratorWrapper.Reset();
          this.Panel.InitializeDataSourceEnumerator( m_dataSourceEnumeratorWrapper );
          m_dataEnumeratorInitialized = true;
        }

        return m_dataSourceEnumeratorWrapper;
      }
    }

    #endregion

    #region RequestsPending Internal Property

    internal bool RequestsPending
    {
      get
      {
        return m_pendingRequest;
      }
      set
      {
        if( m_pendingRequest != value )
        {
          m_pendingRequest = value;
          this.UpdateResquestStatusStates( true );
        }
      }
    }

    private bool m_pendingRequest = false;

    #endregion

    #region SelectionChanged Public Event

    public event EventHandler SelectionChanged;

    private void RaiseSelectionChanged()
    {
      if( this.SelectionChanged != null )
        this.SelectionChanged( this, EventArgs.Empty );
    }

    #endregion

    #region AllowDrag Public Property

    public static readonly DependencyProperty AllowDragProperty = DependencyProperty.Register(
      "AllowDrag",
      typeof( bool ),
      typeof( ListBox ),
      new PropertyMetadata( false ) );

    public bool AllowDrag
    {
      get
      {
        return ( bool )this.GetValue( ListBox.AllowDragProperty );
      }
      set
      {
        this.SetValue( ListBox.AllowDragProperty, value );
      }
    }

    #endregion

    #region DragStarting Public Event

    internal void OnDragStarting( DragDropCancelEventArgs e )
    {
      if( this.DragStarting != null )
        this.DragStarting( this, e );
    }

    public event EventHandler<DragDropCancelEventArgs> DragStarting;

    #endregion

    #region NavigateToGroupCommand Read-Only Property

    public ICommand NavigateToGroupCommand
    {
      get
      {
        if( m_navigateToGroupCommandCache == null )
        {
          m_navigateToGroupCommandCache = new Command(
            new Action<object>( this.OnNavigateToGroupExecuted ),
            new Func<object, bool>( this.OnNavigateToGroupCanExecute ) );
        }

        return m_navigateToGroupCommandCache;
      }
    }

    #endregion

    #region ShowDataNavigationPaneCommand Property
    public ICommand ShowDataNavigationPaneCommand
    {
      get
      {
        if( m_showDataNavigationPane == null )
        {
          m_showDataNavigationPane = new Command(
            new Action<object>( this.OnShowDataNavigationPaneExecute ),
            new Func<object, bool>( this.OnShowDataNavigationPaneCanExecute ) );
        }

        return m_showDataNavigationPane;
      }
    }
    #endregion

    #region HideDataNavigationPaneCommand Property
    public ICommand HideDataNavigationPaneCommand
    {
      get
      {
        if( m_hideDataNavigationPane == null )
        {
          m_hideDataNavigationPane = new Command(
            new Action<object>( this.OnHideDataNavigationPaneExecute ),
            new Func<object, bool>( this.OnHideDataNavigationPaneCanExecute ) );
        }

        return m_hideDataNavigationPane;
      }
    }
    #endregion

    #region ParentListBox Attached Property

    public static readonly DependencyProperty ParentListBoxProperty =
        DependencyProperty.RegisterAttached( "ParentListBox", typeof( ListBox ), typeof( ListBox ), new PropertyMetadata( null, ListBox.OnParentListBoxChanged ) );

    public static ListBox GetParentListBox( DependencyObject obj )
    {
      return ( ListBox )obj.GetValue( ListBox.ParentListBoxProperty );
    }

    internal static void SetParentListBox( DependencyObject obj, ListBox value )
    {
      obj.SetValue( ListBox.ParentListBoxProperty, value );
    }

    private static void OnParentListBoxChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      PanelBase panel = sender as PanelBase;

      if( panel != null )
      {
        panel.OnParentListBoxChanged( sender as PanelBase, e );
      }
    }

    #endregion

    #region InternalAllowDrop Property

    private static readonly DependencyProperty InternalAllowDropProperty = DependencyProperty.Register(
      "InternalAllowDrop",
      typeof( bool ),
      typeof( ListBox ),
      new PropertyMetadata(
        ( bool )false,
        new PropertyChangedCallback( ListBox.OnInternalAllowDropChanged ) ) );

    private static void OnInternalAllowDropChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBox listBox = sender as ListBox;

      if( listBox == null )
        return;

      listBox.OnAllowDropChanged();
    }

    #endregion

    #region IsLocked Internal Property

    internal bool IsLocked
    {
      get;
      private set;
    }

    #endregion

    #region Public Methods

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      ScrollViewer scrollViewer = this.GetTemplateChild( "ScrollViewer" ) as ScrollViewer;

      ItemsPresenter itemsPresenter = null;

      if( scrollViewer != null )
      {
        itemsPresenter = scrollViewer.Content as ItemsPresenter;

#if SILVERLIGHT
        scrollViewer.TabNavigation = KeyboardNavigationMode.Once;
#else
        scrollViewer.SetValue( KeyboardNavigation.TabNavigationProperty, KeyboardNavigationMode.Once );
        scrollViewer.CanContentScroll = true;
#endif
      }
      else
      {
        // In case no scrollViewer is defined in the ControlTemplate
        itemsPresenter = ListBox.TryGetItemsPresenter( this );
      }

#if !WINDOWS_PHONE
      RichTextBox licensingMessageRichTextBox = this.GetTemplateChild( "LicensingMessageRichTextBox" ) as RichTextBox;

      if( licensingMessageRichTextBox != null )
      {
        Paragraph paragraph = new Paragraph();

        Run startRun = new Run();
        startRun.Text = "A valid license key must be provided in order to license Xceed Ultimate ListBox for Silverlight. Refer to the ";
        paragraph.Inlines.Add( startRun );

        Run hyperlinkRun = new Run();
        hyperlinkRun.Text = "Licensing topic";
        Hyperlink hyperlink = new Hyperlink();
        hyperlink.Inlines.Add( hyperlinkRun );
        hyperlink.NavigateUri = new Uri( "http://doc.xceedsoft.com/products/XceedSilverlightListBox/Licensing.html" );
        hyperlink.TargetName = "_blank";
        hyperlink.Foreground = new SolidColorBrush( Colors.White );
#if SILVERLIGHT
        hyperlink.MouseOverForeground = new SolidColorBrush( Color.FromArgb( 255, 85, 0, 0 ) );
#endif
        paragraph.Inlines.Add( hyperlink );

        Run endRun = new Run();
        endRun.Text = " in the documentation for more information.";
        paragraph.Inlines.Add( endRun );

#if SILVERLIGHT
        licensingMessageRichTextBox.Blocks.Add( paragraph );
#else
        licensingMessageRichTextBox.Document.Blocks.Clear();
        licensingMessageRichTextBox.Document.Blocks.Add( paragraph );
#endif
      }
#endif

      PanelBase panel = null;

      if( itemsPresenter != null )
      {
        itemsPresenter.InitializeAndLoadItemsPanelTemplate();

        panel = itemsPresenter.Content as PanelBase;
      }

      if( panel == null )
        throw new ListBoxException( "No ItemsPanel was specified or the ItemsPanel is not a type derived from RecyclingPanel." );

      ListBox.SetParentListBox( panel, this );

      this.Panel = panel;

      // Defer the first UpdateViewportContent until the ArrangeOverride 
      // since some properties are set during the MeasureOverride
      m_onApplyTemplateViewportUpdateDisposable = panel.LayoutManager.DeferUpdateViewport();

      this.UpdateDataSourceAndDataSourceEnumerator();
      this.ChangeVisualState( false );
    }

    public virtual DependencyObject GetContainerFromItem( object item )
    {
      if( item == null )
        throw new ArgumentNullException( "item" );

      PanelBase panel = this.Panel;

      if( panel == null )
        return null;

      return panel.GetContainerFromItem( item );
    }

    public void ScrollIntoView( object item )
    {
      if( item == null )
        throw new ArgumentNullException( "item" );

      PanelBase panel = this.Panel;

      if( panel == null )
        return;

      panel.BringItemIntoView( item );
    }

    public void Refresh()
    {
      m_forceRefresh = true;
      try
      {
        this.RefreshCore();
      }
      finally
      {
        m_forceRefresh = false;
      }
    }

    public void SelectAll()
    {
      m_selectionManager.SelectAllHelper();
    }

    public IAsyncResult BeginGetSelectedItems( AsyncCallback callback, object asyncState )
    {
      IList<SelectionRange> selectionRanges = this.SelectedRanges;

      // Prevent the selected items to be retrieved in design mode.
      if( m_isInDesigner )
      {
        selectionRanges = new SelectionRange[ 0 ];
      }

      SelectedItemsAsyncResult asyncResult = new SelectedItemsAsyncResult( selectionRanges, callback, asyncState, this.DataSourceWrapper );

      asyncResult.Launch();

      return asyncResult;
    }

    public IEnumerable<object> EndGetSelectedItems( IAsyncResult asyncResult )
    {
      if( asyncResult == null )
        throw new ArgumentNullException( "asyncResult" );

      SelectedItemsAsyncResult selectedItemsAsyncResult = asyncResult as SelectedItemsAsyncResult;

      if( selectedItemsAsyncResult == null )
        throw new ArgumentException( "The specified asyncResult must be a SelectedItemAsyncResult.", "asyncResult" );

      return selectedItemsAsyncResult.SelectedItems;
    }

    #endregion Public Methods

    #region Protected Methods

    internal void OnIsEnabledChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
      this.UpdateCommonStates( true );
    }

    protected virtual bool OnShowDataNavigationPaneCanExecute( object parameter )
    {
      DataNavigationConfiguration dataNavigationConfiguration =
        this.GetValue( DataNavigationControl.DataNavigationConfigurationProperty ) as DataNavigationConfiguration;

      return ( dataNavigationConfiguration != null );
    }

    protected virtual void OnShowDataNavigationPaneExecute( object parameter )
    {
      m_dataNavigationPaneVisibility = Visibility.Visible;
      this.UpdateDataNavigationPaneStates( true );
    }

    protected virtual bool OnHideDataNavigationPaneCanExecute( object parameter )
    {
      return true;
    }

    protected virtual void OnHideDataNavigationPaneExecute( object parameter )
    {
      m_dataNavigationPaneVisibility = Visibility.Collapsed;
      this.UpdateDataNavigationPaneStates( true );
    }

    protected virtual bool OnNavigateToGroupCanExecute( object parameter )
    {
      if( ( this.GroupDescriptions.Count == 0 ) || ( parameter == null ) )
        return false;

      return true;
    }

    protected virtual void OnNavigateToGroupExecuted( object parameter )
    {
      this.NavigateToGroup( parameter );
    }

    protected override Size ArrangeOverride( Size finalSize )
    {
      if( m_onApplyTemplateViewportUpdateDisposable != null )
      {
        m_onApplyTemplateViewportUpdateDisposable.Dispose();
        m_onApplyTemplateViewportUpdateDisposable = null;
      }

      return base.ArrangeOverride( finalSize );
    }

    protected override void OnKeyDown( KeyEventArgs e )
    {
      base.OnKeyDown( e );

      if( ( ( Keyboard.Modifiers & ModifierKeys.Control ) == ModifierKeys.Control )
          && ( e.Key == Key.A ) )
      {
        this.SelectAll();
      }
    }

    protected override void OnGotFocus( RoutedEventArgs e )
    {
      base.OnGotFocus( e );

      if( e.OriginalSource != this )
        return;

      PanelBase panel = this.Panel;

      if( panel == null )
        return;

#if SILVERLIGHT
      FrameworkElement focusedElement = FocusManager.GetFocusedElement() as FrameworkElement;
#else
      FrameworkElement focusedElement = FocusManager.GetFocusedElement( this ) as FrameworkElement;
#endif

      if( focusedElement == null )
        return;

      if( !focusedElement.IsDescendant( this ) )
        return;

      DataPath currentDataPath = this.CurrentDataPath;

      if( currentDataPath == null )
        return;

      ListBoxContainer currentContainer = panel.LayoutManager.GetContainerFromPath( currentDataPath );

      if( currentContainer == null )
        return;

      currentContainer.Focus();
    }

    protected internal virtual void PrepareContainerForItem( ListBoxContainer container, object item )
    {
      ListBox.SetParentListBox( container, this );

      DataPath itemDataPath = item as DataPath;

      if( itemDataPath == null )
      {
        Debug.Assert( false, "Create a DataPath from the item" );
      }

      container.DataPath = itemDataPath;
      container.SetDataContextFromDataPath( itemDataPath );

      SelectionRange itemSelectionRange = null;

      // If the DataPath is not loaded, an ItemReplace will be received
      // which will force another call to PrepareContainer for this DataPath
      // and ensure its selection state.
      if( container.IsSelectable && ( itemDataPath.LastChild.IsLoaded ) )
      {
        // Update the SelectionRange and IsSelected when preparing
        // a container to ensure its visual state is correctly set
        itemSelectionRange = SelectionHelper.FindSelectionRangeForPath( m_dataSourceWrapper.DataHelper,
          itemDataPath,
          this.SelectedRanges );
      }

      bool isSelected = ( itemSelectionRange != null )
                     && ( itemSelectionRange.SelectionType == SelectionType.Selection );

      container.SelectionRange = itemSelectionRange;
      container.SetIsSelectedInternal( isSelected );

      Type containerType = container.GetType();
      ConfigurationType configurationType = ConfigurationType.Item;

      if( typeof( ListBoxItem ).IsAssignableFrom( containerType ) )
      {
        configurationType = ConfigurationType.Item;
      }
      else if( typeof( ListBoxGroupHeader ).IsAssignableFrom( containerType ) )
      {
        configurationType = ConfigurationType.GroupHeader;
      }
      else
      {
        throw new ListBoxInternalException( "Any other container configuration type is invalid." );
      }

      // Get a calculated configuration for the container type.
      ListBoxContainerConfigurationBase configuration = this.GetCachedContainerConfiguration( configurationType );

      // Set the ContainerLayoutMode used by the panel/active view to layout the container.
      container.PositioningLayoutMode = this.GetPositioningContainerLayoutMode();
      // Set the layout mode to the one used by the panel to layout the container.  This will be the default
      // if the container's configuration does not change it during the call to the configuration's PrepareContainerForItem.
      ListBoxContainer.SetLayoutMode( container, container.PositioningLayoutMode );

#if !SILVERLIGHT
      container.InvalidateMeasureRecursively();
#endif

      // Apply the configuration.
      configuration.PrepareContainerForItem( container, item );

      container.IsDirty = false;
    }

    protected internal virtual void ClearContainerForItem( ListBoxContainer container )
    {
      container.ClearValue( ListBox.ParentListBoxProperty );

      container.DataContext = null;
      container.Content = null;
      container.DataPath = null;
      container.SelectionRange = null;
      container.SetIsSelectedInternal( false );
      container.IsMouseOver = false;
    }

    internal void Reset()
    {
      if( !this.IsInitialized )
        return;

      if( this.Panel != null )
        this.Panel.ResetPanel();

      // Ensure to use the UnselectAllHelper and not just
      // clearing the SelectedRanges in order to correctly
      // update the containers, SelectedItem and SelectedItems
      m_selectionManager.UnselectAllHelper();

      this.UpdateDataSourceAndDataSourceEnumerator();

      // Ensure to update the ItemsSource on the ItemsWrapper
      // collection in order to correctly support the Unbound
      // mode
      if( this.IsUnbound )
      {
        ItemsWrapper itemsWrapper = this.Items as ItemsWrapper;
        Debug.Assert( itemsWrapper != null );
        itemsWrapper.UpdateItemsSource();
      }
    }

    internal void OnItemTemplateChanged( DataTemplate dataTemplate, DataTemplate dataTemplate_2 )
    {
    }

    internal void OnOrientationChanged( Orientation oldValue, Orientation newValue )
    {
    }

    internal ListBoxContainerConfigurationBase GetContainerFactory( ConfigurationType configurationType )
    {
      return this.GetCachedContainerConfiguration( configurationType );
    }

    internal void OnListBoxContainerIsSelectedChanged( ListBoxContainer container )
    {
      if( ( container != null ) && ( container.IsSelected ) )
      {
        this.CurrentDataPath = container.DataPath;
      }
    }

    internal void ChangeVisualState( bool useTransitions )
    {
      this.UpdateLicensingStates( useTransitions );

      this.UpdateCommonStates( useTransitions );

      this.UpdateResquestStatusStates( useTransitions );

      this.UpdateDataNavigationPaneStates( useTransitions );
    }

    #endregion Protected Methods

    #region Internal Methods

    internal void UpdateSelection( ListBoxContainer container, SelectionAction action )
    {
      // Never update selection if a null container is updated or if its DataPath is not yet loaded
      if( ( container != null ) && ( container.DataPath != null ) && ( !container.DataPath.LastChild.IsLoaded ) )
        return;

      m_selectionManager.UpdateSelectionHelper( container, action );
    }

    internal void ChangeVisualState()
    {
      this.ChangeVisualState( true );
    }

    internal bool GoToState( bool useTransitions, string stateName )
    {
      return VisualStateManager.GoToState( this, stateName, useTransitions );
    }

    internal void ClearDataSourceEnumeratorWrapper()
    {
      if( m_dataSourceEnumeratorWrapper != null )
      {
        m_dataSourceEnumeratorWrapper.DataChanged -= this.OnDataEnumerator_DataChanged;
        m_dataSourceEnumeratorWrapper.HasPendingRequestsChanged -= this.OnDataEnumerator_HasPendingRequestsChanged;
      }

      m_dataSourceEnumeratorWrapper = null;
    }

    internal void NavigateToNextGroup( object group )
    {
      this.NavigateToGroup( group, GroupTarget.Next );
    }

    internal void NavigateToPreviousGroup( object group )
    {
      this.NavigateToGroup( group, GroupTarget.Previous );
    }

    internal void NavigateToGroup( object group )
    {
      GroupDataContext groupDataContext = group as GroupDataContext;

      if( groupDataContext != null )
        group = groupDataContext.GroupValue;

      DataPath groupDataPath = this.CreateGroupDataPathFromGroupKey( group );

      if( groupDataPath == null )
        return;

      this.NavigateToGroup( GroupTarget.Current, groupDataPath );

      m_dataNavigationPaneVisibility = Visibility.Collapsed;
      this.UpdateDataNavigationPaneStates( true );

    }

    internal IDisposable DeferUpdateSelection()
    {
      return new DeferUpdateSelectedItemAndItems( this );
    }

    #endregion Internal Methods

    #region Private Properties

    private BindingPathValueExtractor SelectedValuePathExtractor
    {
      get
      {
        if( m_selectedValuePathExtractor == null )
        {
          m_selectedValuePathExtractor = new BindingPathValueExtractor( new PropertyPath( m_cachedSelectedValuePath ) );
        }

        return m_selectedValuePathExtractor;
      }
    }

    private BindingPathValueExtractor m_selectedValuePathExtractor;

    #endregion

    #region Private Methods

    private bool CanExecuteNextView( object parameter )
    {


      return false;
    }

    private void ExecuteNextView( object parameter )
    {






    }

    private bool CanExecutePreviousView( object parameter )
    {


      return false;
    }

    private void ExecutePreviousView( object parameter )
    {






    }

    private DataPath CreateGroupDataPathFromGroupKey( object group )
    {
      if( group == null )
        return null;

      Debug.Assert( this.GroupDescriptions.Count > 0 );

      GroupDataItem groupDataItem = new GroupDataItem( group );

      return new DataPath( groupDataItem );
    }

    private void NavigateToGroup( object parameter, GroupTarget groupTarget )
    {
      if( parameter == null )
        return;

      // Optimization to avoid the Panel to create the DataPath itself
      GroupDataContext groupDataContext = parameter as GroupDataContext;

      if( groupDataContext == null )
        return;

      DataPath groupDataPath = groupDataContext.DataPath;

      if( groupDataPath == null )
        return;

      this.NavigateToGroup( groupTarget, groupDataPath );

    }

    private void NavigateToGroup( GroupTarget target, DataPath groupDataPath )
    {
      if( this.Panel == null )
        throw new ListBoxInternalException( "Panel shouln't be null while navigating across groups." );

      DataSourceEnumeratorWrapper dataSourceEnumeratorWrapper = this.DataSourceEnumeratorWrapper;

      if( dataSourceEnumeratorWrapper == null )
        return;

      GroupFetchResult fetchResult = null;
      try
      {
        fetchResult = dataSourceEnumeratorWrapper.FetchGroup( groupDataPath, target, 1 );
      }
      catch( Exception exception )
      {
        throw new InvalidOperationException( "An error occurred trying to fetch a group. If using a DataNavigationConfiguration, " +
          "make sure that the data type of the added keys matches the first group description property's data type. See inner exception for details.",
          exception );
      }

      if( fetchResult == null )
      {
        Debug.Assert( false, "FetchGroup returned a null fetch result." );
        return;
      }

      if( fetchResult.EndPositionObject.IsAfterLast )
      {
        // If we reach the end of the data enumrator
        // Move the enumerator back and get the DataPath of the last valid group
        this.Panel.EnumeratorHelper.Move( -1 );

        DataPath currentDataPath = this.Panel.EnumeratorHelper.CurrentDataPath;
        DataPath validGroupPath = currentDataPath.CreateAncestorPath( groupDataPath.Depth );
        fetchResult = dataSourceEnumeratorWrapper.FetchGroup( validGroupPath, GroupTarget.Current, 1 );
      }

      ICollection<DataPath> fetchedItems = fetchResult.FetchedItems;

      if( ( fetchedItems != null ) && ( fetchedItems.Count > 0 ) )
      {
        this.Panel.JumpToPath( fetchedItems.First(), null, target, false );
      }
    }

    private ContainerLayoutMode GetPositioningContainerLayoutMode()
    {
      PanelBase panel = this.Panel;

      if( panel == null )
        throw new InvalidOperationException( "An invalid operation occurred. ItemsPanel is null." );

      ContainerLayoutMode containerLayoutMode = panel.GetPositioningContainerLayoutMode();

      if( containerLayoutMode == ContainerLayoutMode.Default )
        throw new InvalidOperationException( "An invalid operation occurred. The Panel's GetPositioningContainerLayoutMode should never return ContainerLayoutMode.Default." );

      return containerLayoutMode;
    }

    private void DeferRefresh()
    {
      // Only dispatch a call to Refresh if there is not already one dispatched.
      if( m_refreshDispatcherOperation == null )
      {
        m_refreshDispatcherOperation = this.Dispatcher.BeginInvoke( new Action( this.RefreshCore ) );
      }
    }

    private void RefreshCore()
    {
      if( ( !m_forceRefresh ) && ( m_refreshDispatcherOperation == null ) )
      {
        // A call to Refresh was called after the dispatching but before
        // the invoking.
        return;
      }

      PanelBase panel = this.Panel;

      if( panel != null )
      {
        panel.Refresh();
      }

      m_refreshDispatcherOperation = null;
    }

    private void OnSelectionManagerSelectionChanged( object sender, EventArgs e )
    {
      this.UpdateSelectedItemAndSelectedItemsAndRaiseSelectionChanged();
    }

    private void OnSelectionManagerUpdateSelectionProperties( object sender, EventArgs e )
    {
      if( this.Panel != null )
        this.Panel.UpdateSelectionPropertiesOnContainers();
    }

    private void OnDataEnumerator_HasPendingRequestsChanged( object sender, EventArgs e )
    {
      this.RequestsPending = !this.RequestsPending;
    }

    private void OnDataEnumerator_DataChanged( object sender, DataChangedEventArgs e )
    {
      if( this.Panel == null )
        return;

      this.Panel.OnDataEnumerator_DataChanged( sender, e );

      // Force an update of SelectedItemAndSelectedItems after a change in the 
      // data source to ensure removed items are no longer present in the
      // SelectedItem or SelectedItems and new items within selection ranges
      // are added.
      switch( e.Action )
      {
        case DataChangedAction.Add:
        case DataChangedAction.ItemChanged:
        case DataChangedAction.Remove:
        case DataChangedAction.Replace:
        case DataChangedAction.Reset:
          this.Dispatcher.BeginInvoke( new Action( this.UpdateSelectedItemAndSelectedItemsAndRaiseSelectionChanged ) );
          break;
      }
    }

    private static ItemsPresenter TryGetItemsPresenter( FrameworkElement parent )
    {
      if( parent == null )
        return null;

      ItemsPresenter itemsPresenter = parent as ItemsPresenter;

      if( itemsPresenter != null )
        return itemsPresenter;

      int childrenCount = VisualTreeHelper.GetChildrenCount( parent );

      for( int i = 0; i < childrenCount; i++ )
      {
        FrameworkElement child = VisualTreeHelper.GetChild( parent, i ) as FrameworkElement;

        itemsPresenter = child as ItemsPresenter;

        if( itemsPresenter != null )
          return itemsPresenter;

        itemsPresenter = ListBox.TryGetItemsPresenter( child );

        if( itemsPresenter != null )
          return itemsPresenter;
      }

      return null;
    }

    private void UpdateDataSourceAndDataSourceEnumerator()
    {
      Debug.Assert( this.IsInitialized );

      // Get the ItemsSource or revert to 
      // Items if the ItemsSource is null
      // to be in "Unbound mode"
      // Note that when binding the ListBox ItemSource property, the ListBox can be
      // unbound for a short moment when EndInit is called but before the binding has resolved.
      object itemsSource = this.ItemsSource;

      if( itemsSource == null )
      {
        itemsSource = this.Items;
        this.IsUnbound = true;
      }
      else
      {
        this.IsUnbound = false;
      }

      if( m_cachedItemsSource != itemsSource )
      {
        try
        {
          Debug.Assert( itemsSource != null );

          this.IsAsyncDataSource = false;

          m_dataSourceWrapper = DataSourceHelper.GenerateDataSourceForObject( itemsSource, this.Dispatcher );

          Debug.Assert( m_dataSourceWrapper.Provider != null );

          this.IsAsyncDataSource = m_dataSourceWrapper.Provider.IsAsynchronous;

          m_cachedItemsSource = itemsSource;
        }
        catch( Exception exception )
        {
          throw new InvalidOperationException(
            "An internal error occurred generating the data source wrapper. " +
            "Make sure that the ItemsSource property is set to an IEnumerable instance or to an Xceed.Silverlight.Data.DataSourceProvider instance.",
            exception );
        }
      }

      // Never apply Grouping, Sorting, or Filtering to an unbound listbox.
      // Else the list is untyped and the data source wrapper won't be able to find
      // the properties corresponding to the grouping, sorting, or filtering criterias
      // resulting in a crash.
      if( !this.IsUnbound )
      {
        m_dataSourceWrapper.Filter = m_filter;
        m_dataSourceWrapper.FilterExpression = this.FilterExpression;

        this.UpdateDataSourceWrapperGroupDescriptions();
        this.UpdateDataSourceWrapperSortDescriptions();
      }

      this.ClearDataSourceEnumeratorWrapper();

      m_dataSourceEnumeratorWrapper = m_dataSourceWrapper.GetSyncDataEnumerator();
      m_dataSourceEnumeratorWrapper.DataChanged += this.OnDataEnumerator_DataChanged;
      m_dataSourceEnumeratorWrapper.HasPendingRequestsChanged += this.OnDataEnumerator_HasPendingRequestsChanged;

      if( m_dataSourceEnumeratorWrapper.IsCachingEnumerator )
      {
        m_dataSourceEnumeratorWrapper.SetCacheSize( 100 );
      }

      this.RequestsPending = false;
      m_dataEnumeratorInitialized = false;
    }

    private void UpdateDataSourceWrapperGroupDescriptions()
    {
      DataSourceWrapper dataSourceWrapper = this.DataSourceWrapper;

      // GroupDescriptions will be applied when the DataSourceWrapper
      // is initialized
      if( dataSourceWrapper == null )
        return;

      // Never apply Grouping, Sorting, or Filtering to an unbound listbox.
      // Else the list is untyped and the data source wrapper won't be able to find
      // the properties corresponding to the grouping, sorting, or filtering criterias
      // resulting in a crash.
      Debug.Assert( ( this.IsInitialized ) && ( !this.IsUnbound ) );

      ListBox.SyncCollections( this.GroupDescriptions, dataSourceWrapper.GroupDescriptions );
      this.ClearDataSourceEnumeratorWrapper();
    }

    private void UpdateDataSourceWrapperSortDescriptions()
    {
      DataSourceWrapper dataSourceWrapper = this.DataSourceWrapper;

      // SortDescriptions will be applied when the DataSourceWrapper
      // is initialized
      if( dataSourceWrapper == null )
        return;

      // Never apply Grouping, Sorting, or Filtering to an unbound listbox.
      // Else the list is untyped and the data source wrapper won't be able to find
      // the properties corresponding to the grouping, sorting, or filtering criterias
      // resulting in a crash.
      Debug.Assert( ( this.IsInitialized ) && ( !this.IsUnbound ) );

      ListBox.SyncCollections( this.SortDescriptions, dataSourceWrapper.SortDescriptions );
      this.ClearDataSourceEnumeratorWrapper();
    }

    private static void SyncCollections( IList listBoxCollection, IList dataSourceCollection )
    {
      if( dataSourceCollection == null )
        throw new ArgumentNullException( "dataSourceCollection" );

      if( listBoxCollection == null )
        throw new ArgumentNullException( "listBoxCollection" );

      dataSourceCollection.Clear();

      int listBoxCollectionCount = listBoxCollection.Count;

      for( int i = 0; i < listBoxCollectionCount; i++ )
      {
        dataSourceCollection.Add( listBoxCollection[ i ] );
      }
    }

    private void UpdateSelectedRangesFromSelectedItem()
    {
      DataSourceWrapper dataSourceWrapper = this.DataSourceWrapper;

      if( ( this.IsInitialized )
          && ( !this.IsUpdateSelectedItemsAndItemInhibited )
          && ( dataSourceWrapper != null ) )
      {
        object selectedItem = this.SelectedItem;

        if( selectedItem != null )
        {
          DataPath dataPath = dataSourceWrapper.CreateDataPathForObject( selectedItem );

          ListBoxContainer container = null;

          if( this.Panel != null )
            container = this.Panel.LayoutManager.GetContainerFromPath( dataPath, true );

          this.SelectionManager.UpdateSingleSelectionHelper( container, dataPath );
        }
        else
        {
          this.UpdateSelectedItemAndSelectedItems();
        }
      }
    }

    private void UpdateSelectedItemAndSelectedItems()
    {
      if( this.IsUpdateSelectedItemsAndItemInhibited )
        return;

      using( this.DeferUpdateSelection() )
      {
        IList<SelectionRange> selectedRanges = this.SelectedRanges;
        int selectedRangeCount = selectedRanges.Count;

        if( selectedRangeCount > 0 )
        {
          if( ( this.SelectionMode == SelectionMode.Single ) && ( selectedRangeCount > 1 ) )
            throw new ListBoxInternalException( "More than one range selected while in SelectionMode.Single." );

          // Always treat the first item of the first selectionRange as the SelectedItem
          SelectionRange range = selectedRanges[ 0 ];

          if( ( this.SelectionMode == SelectionMode.Single ) && !range.IsSingleItem )
            throw new ListBoxInternalException( "A range with multiple items is selected while in SelectionMode.Single." );

          // If the update was completed synchronously, the update is completed
          // else, wait for the operation to complete and process
          // everything in the callback
          if( !m_selectedItems.UpdateSelectedItems() )
            return;
        }
        else
        {
          ( ( IList )m_selectedItems ).Clear();
        }

        this.UpdateSelectedItem();
      }
    }

    private void UpdateSelectedItemAndSelectedItemsAndRaiseSelectionChanged()
    {
      this.UpdateSelectedItemAndSelectedItems();
      this.RaiseSelectionChanged();
    }

    private void OnSelectedItemsCompleted( object sender, EventArgs e )
    {
      this.UpdateSelectedItem();
    }

    private void UpdateSelectedItem()
    {
      IList selectedItemsIList = m_selectedItems as IList;

      this.SelectedItem = ( selectedItemsIList.Count > 0 )
       ? selectedItemsIList[ 0 ]
       : null;
    }

    private void UpdateLicensingStates( bool useTransitions )
    {
      if( m_isLicensed )
      {
        VisualStateManager.GoToState( this, "Licensed", useTransitions );
      }
      else
      {
        VisualStateManager.GoToState( this, "NotLicensed", useTransitions );
      }
    }

    private void UpdateCommonStates( bool useTransitions )
    {
      if( !base.IsEnabled )
      {
        this.GoToState( useTransitions, "Disabled" );
      }
      else
      {
        this.GoToState( useTransitions, "Normal" );
      }
    }

    private void UpdateResquestStatusStates( bool useTransitions )
    {
      if( m_pendingRequest )
      {
        this.GoToState( useTransitions, "RequestsPending" );
      }
      else
      {
        this.GoToState( useTransitions, "NoPendingRequests" );
      }
    }

    private void UpdateDataNavigationPaneStates( bool useTransition )
    {
      if( m_dataNavigationPaneVisibility == Visibility.Visible )
      {
        this.GoToState( useTransition, "DataNavigationPaneVisible" );
      }
      else
      {
        this.GoToState( useTransition, "DataNavigationPaneHidden" );
      }

    }
    #endregion Private Methods

    #region Private Event Handlers

    private void OnLoaded( object sender, RoutedEventArgs e )
    {
      m_isLoaded = true;
      this.ChangeVisualState( false );
    }

    private void OnUnLoaded( object sender, RoutedEventArgs e )
    {
      m_isLoaded = false;
      this.ChangeVisualState( false );
    }

    private void OnAllowDropChanged()
    {
    }

    #endregion Private Event Handlers

    #region Private Nested Class
    private class DeferUpdateSelectedItemAndItems : IDisposable
    {
      public DeferUpdateSelectedItemAndItems( ListBox listBox )
      {
        if( listBox == null )
          throw new NullReferenceException( "ListBox should not be null." );

        m_listBox = listBox;
        m_listBox.m_inhibitUpdateSelectedItemAndItemsCount++;
      }

      public void Dispose()
      {
        m_listBox.m_inhibitUpdateSelectedItemAndItemsCount--;
        m_listBox = null;
      }

      private ListBox m_listBox;
    }
    #endregion

    #region Private Fields

    private Predicate<object> m_filter;
    private bool m_isLoaded;

    private DispatcherOperation m_refreshDispatcherOperation;
    internal bool m_dataEnumeratorInitialized;
    private bool m_forceRefresh;
    private int m_inhibitUpdateSelectedItemAndItemsCount;
    private int m_iSupportInitializeCount;

    private Visibility m_dataNavigationPaneVisibility = Visibility.Collapsed;

    private SelectionManager m_selectionManager;
    private DataSourceWrapper m_dataSourceWrapper;
    private DataSourceEnumeratorWrapper m_dataSourceEnumeratorWrapper;
    private SelectedItemsWrapper m_selectedItems;
    private ItemsWrapper m_items;
    private object m_cachedItemsSource;

    private string m_cachedSelectedValuePath;
    private bool m_updatingSelectedItemFromSelectedValue;
    private bool m_updatingSelectedValueFromSelectedItem;

    private IDisposable m_onApplyTemplateViewportUpdateDisposable;
    private Command m_navigateToGroupCommandCache;

    private Command m_showDataNavigationPane;
    private Command m_hideDataNavigationPane;

    private Command m_nextView;
    private Command m_previousView;

    //licensing
    private readonly bool m_isInDesigner;
    private bool m_isLicensed = true;

    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    internal void RaisePropertyChanged( string propertyName )
    {
      if( this.PropertyChanged == null )
        return;

      if( propertyName == null )
        propertyName = string.Empty;

      this.PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
    }

    #endregion

    #region ISupportInitialize Members

    internal bool IsInitialized
    {
      get
      {
        return ( m_iSupportInitializeCount == 0 );
      }
    }

    void ISupportInitialize.BeginInit()
    {
      m_iSupportInitializeCount++;

      if( m_iSupportInitializeCount == 1 )
      {
        this.BeginInitCore();
      }
    }

    void ISupportInitialize.EndInit()
    {
      if( m_iSupportInitializeCount == 0 )
        return;

      m_iSupportInitializeCount--;

      if( m_iSupportInitializeCount == 0 )
      {
        this.EndInitCore();
      }
    }

    internal void BeginInitCore()
    {
    }

    internal void EndInitCore()
    {
      this.UpdateDataSourceAndDataSourceEnumerator();

      // We must update the Selection Ranges
      // if a SelectedItem was set
      if( this.SelectedItem != null )
      {
        this.UpdateSelectedRangesFromSelectedItem();
      }
      // Or if SelectedItems were defined
      else if( this.SelectedItems.Count > 0 )
      {
        this.UpdateSelectedItemAndSelectedItems();
      }
    }

    #endregion

    #region IWeakEventListener Members

    bool IWeakEventListener.ReceiveWeakEvent( Type managerType, object sender, EventArgs e )
    {
      bool handled = false;

      if( managerType == typeof( ContainerConfigurationsInvalidatedManager ) )
      {
        ConfigurationChangedEventArgs configurationChangedEventArgs = ( ConfigurationChangedEventArgs )e;

        ConfigurationType configurationType = configurationChangedEventArgs.ConfigurationType;

        if( configurationType == ConfigurationType.All )
        {
          // No container type specified, clear all configurations.
          this.ClearContainerConfigurationCache();
        }
        else
        {
          // Clear only the cached configuration for container type.
          this.InvalidateContainerConfigurationCache( configurationType );
        }

        handled = true;
      }
      else if( managerType == typeof( CachedSizeChangedManager ) )
      {
        CachedSizeChangedEventArgs sizeChangedEventArgs = ( CachedSizeChangedEventArgs )e;

        ContainerLayoutMode positioningContainerLayoutMode = this.GetPositioningContainerLayoutMode();
        if( sizeChangedEventArgs.ShouldInvalidateLayout( positioningContainerLayoutMode ) )
        {
          this.DeferRefresh();
        }
      }

      return handled;
    }

    #endregion

    #region IGroupNavigationHost Members

    DataNavigationConfiguration IDataNavigationHost.Configuration
    {
      get
      {
        return DataNavigationControl.GetDataNavigationConfiguration( this );
      }
    }

    void IDataNavigationHost.NavigateToKey( object group )
    {
      this.OnNavigateToGroupExecuted( group );
    }

    ICommand IDataNavigationHost.NavigateToKeyCommand
    {
      get
      {
        return this.NavigateToGroupCommand;
      }
    }

    #endregion

    #region IDropTarget Members

    bool IDropTarget.CanDropElement( DragDropContext dragDropContext )
    {
      return this.OnIDropTargetCanDropElement( dragDropContext );
    }

    void IDropTarget.DragEnter( DragDropContext dragDropContext )
    {
      this.OnIDropTargetDragEnter( dragDropContext );
    }

    void IDropTarget.DragOver( DragDropContext dragDropContext, Point mousePosition )
    {
      this.OnIDropTargetDragOver( dragDropContext, mousePosition );
    }

    void IDropTarget.DragLeave( DragDropContext dragDropContext )
    {
      this.OnIDropTargetDragLeave( dragDropContext );
    }

    void IDropTarget.Drop( DragDropContext dragDropContext )
    {
      this.OnIDropTargetDrop( dragDropContext );
    }

    internal bool OnIDropTargetCanDropElement( DragDropContext dragDropContext )
    {
      if( this.IDropTargetCanDropElement != null )
        return this.IDropTargetCanDropElement( this, new DropEventArgs( dragDropContext ) );

      return false;
    }

    internal void OnIDropTargetDragEnter( DragDropContext dragDropContext )
    {
      if( this.IDropTargetDragEnter != null )
        this.IDropTargetDragEnter( this, new DropEventArgs( dragDropContext ) );
    }

    internal void OnIDropTargetDragOver( DragDropContext dragDropContext, Point mousePosition )
    {
      if( this.IDropTargetDragOver != null )
        this.IDropTargetDragOver( this, new DropEventArgs( dragDropContext ) );
    }

    internal void OnIDropTargetDragLeave( DragDropContext dragDropContext )
    {
      if( this.IDropTargetDragLeave != null )
        this.IDropTargetDragLeave( this, new DropEventArgs( dragDropContext ) );
    }

    internal void OnIDropTargetDrop( DragDropContext dragDropContext )
    {
      if( this.IDropTargetDrop != null )
        this.IDropTargetDrop( this, new DropEventArgs( dragDropContext ) );
    }

    public event CanDropEventHandler IDropTargetCanDropElement;

    public event EventHandler<DropEventArgs> IDropTargetDragEnter;

    public event EventHandler<DropEventArgs> IDropTargetDragOver;

    public event EventHandler<DropEventArgs> IDropTargetDragLeave;

    public event EventHandler<DropEventArgs> IDropTargetDrop;

    #endregion

  }
}
